package com.zhuanzhuan.hero.user.util;

import com.zhuanzhuan.hero.user.config.ThreadPoolConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.function.*;

/**
 * @ClassName AsyncUtil
 * @Description   默认使用默认的线程池，也可指定beanName使用指定的线程池
 * @Author hanjunjun
 * @Date 2024/7/3 17:22
 */
@Component
@Slf4j
public class AsyncUtil {

    private static ThreadPoolExecutor defaultExecutor;

    private static ThreadPoolExecutor userRecentExecutor;

    private static ThreadPoolExecutor userBaseInfoExecutor;

    private static ThreadPoolExecutor homePageInfoExecutor;

    private static ThreadPoolExecutor bizCountExecutor;

    private static ThreadPoolExecutor userDetailExecutor;

    @Autowired
    public AsyncUtil(@Qualifier(ThreadPoolConfiguration.DEFAULT_EXECUTOR) ThreadPoolExecutor defaultExecutor,
                     @Qualifier(ThreadPoolConfiguration.USER_RECENT_EXECUTOR) ThreadPoolExecutor userRecentExecutor,
                     @Qualifier(ThreadPoolConfiguration.USER_BASE_INFO_EXECUTOR) ThreadPoolExecutor userBaseInfoExecutor,
                     @Qualifier(ThreadPoolConfiguration.HOME_PAGE_INFO_EXECUTOR) ThreadPoolExecutor homePageInfoExecutor,
                     @Qualifier(ThreadPoolConfiguration.BIZ_COUNT_EXECUTOR) ThreadPoolExecutor bizCountExecutor,
                     @Qualifier(ThreadPoolConfiguration.USER_DETAIL_EXECUTOR) ThreadPoolExecutor userDetailExecutor) {
        AsyncUtil.defaultExecutor = defaultExecutor;
        log.info("默认线程池加载完成");
        AsyncUtil.userRecentExecutor = userRecentExecutor;
        log.info("近期动态线程池加载完成");
        AsyncUtil.userBaseInfoExecutor = userBaseInfoExecutor;
        log.info("用户基本信息线程池加载完成");
        AsyncUtil.homePageInfoExecutor = homePageInfoExecutor;
        log.info("首页用户信息线程池加载完成");
        AsyncUtil.bizCountExecutor = bizCountExecutor;
        log.info("计数线程池加载完成");
        AsyncUtil.userDetailExecutor = userDetailExecutor;
        log.info("userDetail线程池加载完成");
    }

    /**
     * 异步调用 没有入参没有返回值
     *
     * @param runnable
     * @return java.util.concurrent.CompletableFuture<java.lang.Void>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T> CompletableFuture<Void> run(Runnable runnable) {
        return CompletableFuture.runAsync(runnable, defaultExecutor);
    }

    /**
     * 没有参数，有返回值
     *
     * @param supplier
     * @return java.util.concurrent.CompletableFuture<T>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T> CompletableFuture<T> call(Supplier<T> supplier) {
        return CompletableFuture.supplyAsync(supplier, defaultExecutor);
    }

    public static <T> CompletableFuture<T> call(Supplier<T> supplier, String executorName) {
        return CompletableFuture.supplyAsync(supplier, SpringContextUtil.getBean(executorName, Executor.class));
    }

    /**
     * 异步调用方法，一个参数、有返回值
     *
     * @param function
     * @param param 参数
     * @return java.util.concurrent.CompletableFuture<R>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T, R> CompletableFuture<R> call(Function<T, R> function, T param) {
        return CompletableFuture.supplyAsync(() -> function.apply(param), defaultExecutor);
    }

    /**
     * 异步调用方法，两个参数、有返回值
     *
     * @param biFunction
     * @param param 参数1
     * @param param1
     * @return java.util.concurrent.CompletableFuture<R>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T, U, R> CompletableFuture<R> call(BiFunction<T, U, R> biFunction, T param, U param1) {
        return CompletableFuture.supplyAsync(() -> biFunction.apply(param, param1), defaultExecutor);
    }

    /**
     * 异步调用方法，一个参数、无返回值
     *
     * @param consumer
     * @param param
     * @return java.util.concurrent.CompletableFuture<java.lang.Void>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T> CompletableFuture<Void> run(Consumer<T> consumer, T param) {
        return CompletableFuture.runAsync(() -> consumer.accept(param), defaultExecutor);
    }

    /**
     * 异步调用方法，两个参数、无返回值
     *
     * @param biConsumer
     * @param param
     * @param param1
     * @return java.util.concurrent.CompletableFuture<java.lang.Void>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T, U> CompletableFuture<Void> run(BiConsumer<T, U> biConsumer, T param, U param1) {
        return CompletableFuture.runAsync(() -> biConsumer.accept(param, param1), defaultExecutor);
    }

    /**
     * 指定线程池
     *
     * @param consumer
     * @param param
     * @param executorBeanName 指定线程池beanName
     * @return java.util.concurrent.CompletableFuture<java.lang.Void>
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T> CompletableFuture<Void> run(Consumer<T> consumer, T param, String executorBeanName) {
        Executor executor = SpringContextUtil.getBean(executorBeanName, Executor.class);
        if (Objects.isNull(executor)) {
            throw new RuntimeException("线程池不存在,name:" + executorBeanName);
        }
        return CompletableFuture.runAsync(() -> consumer.accept(param), executor);
    }

    /**
     * 获取结果 不等待
     *
     * @param future
     * @return T
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T> T result(CompletableFuture<T> future)
        throws ExecutionException, InterruptedException, TimeoutException {
        return getResult(future, null, null);
    }

    /**
     * 获取结果   等待一段时间
     *
     * @param future
     * @param timeout
     * @param unit
     * @return T
     * @author  hanjunjun
     * @date 2024/7/6
     **/
    public static <T> T result(CompletableFuture<T> future, long timeout, TimeUnit unit)
        throws ExecutionException, InterruptedException, TimeoutException {
        return getResult(future, timeout, unit);
    }

    public static <T> T result(CompletableFuture<T> future, long timeout, TimeUnit unit, T defaultVal, Object... params){
        T result = null;
        try {
            result =  getResult(future, timeout, unit);
        } catch (Exception e) {
            log.error("get future result err, params:{}", Arrays.toString(params),e);
        }
        return result == null && defaultVal != null ? defaultVal : result;
    }

    private static <T> T getResult(CompletableFuture<T> future, Long timeout, TimeUnit unit)
        throws ExecutionException, InterruptedException, TimeoutException {
        T result;
        try {
            if (Objects.nonNull(timeout) && Objects.nonNull(unit)) {
                result = future.get(timeout, unit);
            } else {
                result = future.get();
            }
        } catch (InterruptedException | ExecutionException | TimeoutException  e) {
            log.error("异步执行获取结果失败，发生异常", e);
            throw e;
        }
        return result;
    }


}
